package com.yz.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.yz.common.properties.JwtProperties;
import com.yz.gateway.config.properties.IgnoreWhiteProperties;
import com.yz.common.constant.CacheConstants;
import com.yz.common.constant.TokenConstants;
import com.yz.common.factory.JedisFactory;
import com.yz.common.resp.BaseResponse;
import com.yz.common.utils.JwtUtils;
import com.yz.common.utils.ResultUtils;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import redis.clients.jedis.Jedis;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created With IntelliJ IDEA.
 * Descriptions:
 * <p>
 * User:Mr.Du
 * Date:2024/3/1
 * Time:16:39
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AuthFilter implements GlobalFilter {

    private final IgnoreWhiteProperties ignoreWhiteProperties;

    private final JwtProperties jwtProperties;

    private final JedisFactory jedisFactory;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        System.out.println(path);
        for(String white : ignoreWhiteProperties.getWhites()){
            if(path.contains(white)){
                System.out.println(1);
                return chain.filter(exchange);
            }
        }
        ServerHttpResponse response = exchange.getResponse();
        // 检查请求中是否包含身份验证的 Token
        String token = exchange.getRequest().getHeaders().getFirst(TokenConstants.AUTHENTICATION);
        System.out.println(token);
        if (StringUtils.isBlank(token)) {
            return unauthorizedResponse(exchange, "令牌不能为空");
        }
        Jedis jedis = jedisFactory.getJedis();
        System.out.println(jwtProperties.getSecretKey());
        Claims claims = JwtUtils.decodeToken(jwtProperties.getSecretKey(), token);
        if(claims == null){
            return unauthorizedResponse(exchange, "token失效");
        }
        String username = (String) claims.get("username");
        String uid = (String) claims.get("uid");
        // token可以解析出来过期时间，完全没必要存储到redis，就先这样叭
        String cache_token = jedis.get(CacheConstants.LOGIN_USER_KEY + username);
        if(StringUtils.isBlank(cache_token)){
            return unauthorizedResponse(exchange, "token失效");
        }
        if(!token.equals(cache_token)){
            return unauthorizedResponse(exchange, "token失效");
        }
        // 将用户信息传递给下游服务
        exchange.getRequest().mutate()
                .header("uid", uid)
                .header("username", username)
                .build();
        long expTime = claims.getExpiration().getTime();
        // 续期
        if (expTime - CacheConstants.REFRESH_TIME <= System.currentTimeMillis()) {
            String renewToken = renewToken(cache_token, jedis);
            if (StringUtils.isNotBlank(renewToken)) {
                response.getHeaders().add(TokenConstants.AUTHENTICATION, renewToken);
            } else {
                return unauthorizedResponse(exchange, "token失效");
            }
        }
        return chain.filter(exchange);
    }

    public String renewToken(String oldToken, Jedis jedis) {
        Claims oldClaims = JwtUtils.decodeToken(jwtProperties.getSecretKey(), oldToken);
        if (oldClaims == null) {
            throw new RuntimeException("系统错误");
        }
        String username = (String) oldClaims.get("username");
        String uid = (String) oldClaims.get("uid");

        Date newExpirationDate = new Date(System.currentTimeMillis() + CacheConstants.EXPIRATION); // 计算新的过期时间
        Map<String, Object> newClaims = new HashMap<>();
        newClaims.put("username", username);
        newClaims.put("uid", uid);
        String newToken = JwtUtils.createToken(jwtProperties.getSecretKey(), null, newClaims, newExpirationDate);
        if(StringUtils.isBlank(newToken)){
            throw new RuntimeException("系统错误");
        }
        jedis.set(CacheConstants.LOGIN_USER_KEY + username, newToken);
        return newToken;
    }


    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg)
    {
        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        return webFluxResponseWriter(exchange.getResponse(), msg, com.yz.common.resp.HttpStatus.UNAUTHORIZED);
    }

    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code)
    {
        return webFluxResponseWriter(response, HttpStatus.OK, value, code);
    }

    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code)
    {
        return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
    }

    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code)
    {
        response.setStatusCode(status);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
        BaseResponse result = ResultUtils.error(code, value.toString());
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }
}

